// scaup - Scala up!
// (c) 2008-2009, Normen Müller, normen.mueller@gmail.com  
// $Id$
package locutor.model.parsing.grgen

import java.io.File
import java.text.MessageFormat.{format}

import scaup.Views._

/** 
 * @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 * @version 0.0.2 */
case class GRGError(message: String) extends Exception(message)

/** This object provides a simple interface to the GRG parser class.
 * 
 * @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 * @version 0.0.2 */
object GRG extends GRGParser {
  def apply(input: File): Any =
    if(input == null || !(input exists))
      throw new GRGError(format("Physical GRG file may not be null (''{0}'').", input))
    else apply(input.content)
  
  def apply(input: String) =
    if(input == null)
      throw new GRGError("Physical GRG representation may not be null.")
    else parse(input)
  
  def parse(input: String) = phrase(grg)(new lexical.Scanner(input)) match {
    case Success(result, _) => result
    case n@_                => throw new GRGError(format("GRG parsing error in ''{0} ...}''\n", input.lines.next) + n)
  }
}

import scala.util.parsing.combinator._
import scala.util.parsing.combinator.lexical._
import scala.util.parsing.input.CharArrayReader.EofCh

/** 
 * @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 * @version 0.0.2 */
private [grgen] class GRGLexer extends StdLexical with RegexParsers {
  override type Elem = Char
  
  override def token: Parser[Token] = 
    ( string                             ^^ StringLit
    | number                             ^^ NumericLit
    | delim
    | EofCh                             ^^^ EOF
    | id                                 ^^ {case i if reserved contains i => Keyword(i) case i => Identifier(i)}        
    | '\"' ~> failure("Unterminated string")
    | failure("Illegal character")
    )
  
//  override def whitespace: Parser[Any] = rep(whitespaceChar | "\\" | '#' ~ rep(chrExcept(EofCh, '\n')))
  
  def string  =      
    ( '\"' ~> rep(charSeq | chrExcept('\"', '\n', EofCh)) <~ '\"' ^^ { _ mkString "" }
    | '\'' ~> rep(charSeq | chrExcept('\'', '\n', EofCh)) <~ '\'' ^^ { _ mkString "" }    
    )
  
  def number  = """-?(\d+(\.\d*)?|\d*\.\d+)([eE][+-]?\d+)?[fFdD]?""".r
  
  def id      = rep(letter | digit | elem("underscore", _=='_')) ^^ { _ mkString "" }
  
  def charSeq = 
    ( '\\' ~ '\"' ^^^ "\"" | '\\' ~ '\\' ^^^ "\\" | '\\' ~ '/'  ^^^ "/" | '\\' ~ 'b'  ^^^ "\b" | '\\' ~ 'f'  ^^^ "\f" 
    | '\\' ~ 'n'  ^^^ "\n" |'\\' ~ 'r'  ^^^ "\r" |'\\' ~ 't'  ^^^ "\t" | '\\' ~ 'u' ~> unicodeBlock
    )
  
  import scala.io.UTF8Codec.{encode}
  def unicodeBlock = hexDigit ~ hexDigit ~ hexDigit ~ hexDigit ^^ {
    case a ~ b ~ c ~ d => new String(encode(Integer.parseInt(List(a, b, c, d) mkString "", 16)))
  }
  
  val hexDigits = Set[Char]() ++ "0123456789abcdefABCDEF".toArray
  
  def hexDigit = elem("hex digit", hexDigits.contains(_))
}

/** 
 * Restrictions:
 * - for example: [prio=1000]:Ant
 * - HEX numbers
 * - ``#include''
 * - alternative
 * 
 * @todo adjust with GrGen.g
 * 
 * @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 * @version 0.0.2 */
private [grgen] class GRGParser extends XGRSParser with ExprParser {
  /* Fill in abstract defs*/
  type Tokens = GRGLexer
  val lexical: Tokens = new GRGLexer
  
/*
 * '?', '?-', '-?', '?--?', '(', ')', '[', ']', '{', '}', ':', '::', ',', '.', '=', '==', '!', '~', '!=',
 * '<<', '>>', '>>>', '/', '+', '+=', '-', '*', '%', '>=', '>', '<=', '<', '->', '<-', '<-->', '<--', '-->',
 * '^', '|', '||', '&', '&&', ';', '\\', '++', '--', '$', 
 * 
 * 'abstract', 'actions', 'alternative', 'arbitrary',
 * 'class', 'connect', 'const'
 * 'delete', 'directed', 'dpo',
 * "edge", "emit", "emitpre", "emitpost", "enum", "eval", "exact", "exec", "extends"
 * "false",
 * "hom"
 * 'if', "in", 'independent', 'induced', 'iterated',
 * "map", 'model', "modify", 'multiple'
 * 'nameof', 'negative', 'node', 'null',
 * 'optional'
 * 'pattern', 'patternpath',
 * 'replace', 'return', 'rule',
 * 'set',
 * 'test', 'true', 'typeof',
 * 'undirected', 'using',
 * 'var', 'visited' 
 */
  
  /* Configure lexical parsing */                                 
  lexical.reserved ++= List(
    "dpo", "delete",
    "exact", "eval", "emit", "exec",
    "hom",
    "induced", "if", "iterated",
    "modify",
    "negative",
    "pattern",
    "rule", "replace", "return",
    "test",
    "using",
    "var"
  ) ++ XGRSParser.reserved ++ ExprParser.reserved
  
  lexical.delimiters ++= List(
    "{", "}", "<-", "->", "?-", "-?", ";", "\\"
  ) ++ XGRSParser.delimiters ++ ExprParser.delimiters
  
  /* Define the grammar */
  def grg = opt(using_stmt) ~ ((action_stmt)*)
  
  def using_stmt = "using" ~ ident ~ (("," ~ ident)*) ~ ";"
  
  def action_stmt = opt("exact" | "induced" | "dpo") ~ (test_stmt | rule_stmt | pattern)
  def test_stmt = "test" ~ ident ~ opt(params) ~ opt(":" ~ return_types) ~ "{" ~ ((pattern)*) ~ "}"  
  def rule_stmt = "rule" ~ ident ~ opt(params) ~ opt(":" ~ return_types) ~ "{" ~ ((pattern)*) ~ (replace | modify) ~ "}"
  def pattern =
    ( ("pattern" ~ opt(ident ~ opt(params) ~ opt(":" ~ return_types)) ~ "{" ~ ((pattern_stmt)*) ~ opt(replace | modify) ~ "}")
    | pattern_stmt
    )
  
  def pattern_stmt: Parser[Any] = 
    ( graphlet 
    | "hom" ~ "(" ~ rep1sep(ident, ",") ~ ")" ~ ";"
    | "iterated" ~ opt(ident) ~ "{" ~ ((pattern_stmt)*) ~ opt(replace | modify) ~ "}"
    | "negative" ~ "{" ~ ((pattern_stmt)*) ~ "}"
    | "if" ~ "{" ~ ((expr ~ ";")*) ~ "}"
    | ("exact" | "induced") ~ "(" ~ repsep(ident, ",") ~ ")"
    | "return" ~ "(" ~ expr ~ (("," ~ expr)*) ~ ")" ~ ";"
    ) 
  
  def replace = "replace" ~ opt(ident) ~ opt(params) ~ "{" ~ ((replace_body)*) ~ "}"  
  def replace_body = 
    ( graphlet
    | eval
    | "return" ~ "(" ~ ident ~ (("," ~ ident)*) ~ ")" ~ ";"
    | "exec" ~ "(" ~ rewrite_seq ~ ")" ~ ";"
    | "emit" ~ "(" ~ expr ~ (("," ~ expr)*) ~ ")" ~ ";"
    )
  
  def modify = "modify" ~ opt(ident) ~ opt(params) ~ "{" ~ ((modify_body)*) ~ "}"  
  def modify_body = 
    ( graphlet
    | eval
    | "delete" ~ "(" ~ ident ~ (("," ~ ident)*) ~ ")" ~ ";"
    | "return" ~ "(" ~ expr ~ (("," ~ expr)*) ~ ")" ~ ";"
    | "exec" ~ "(" ~ rewrite_seq ~ ")" ~ ";"
    | "emit" ~ "(" ~ expr ~ (("," ~ expr)*) ~ ")" ~ ";"
    )
  
  def eval = "eval" ~ "{" ~ eval_body ~ "}"  
  def eval_body = rep(assignment ~ ";")
  
  def graphlet = ((node ~ opt(continuation)) | continuation) ~ ";"
  
  def node =
    ( opt(ident ~ opt("[" ~ ((ident ~ "=" ~ constant) | ident) ~ "]")) ~ ":" ~
        ( ident ~ params
        | (ident | typeOf) ~ opt(type_constraint) ~ opt("<" ~ ident ~ ">")
        )
    | ident ~ opt(params)
    | "."
    )
  
  def continuation: Parser[Any] = edge ~ opt(node ~ opt(continuation))
  
  def edge = 
    ( ("<-" | "-") ~ opt(edge_refinement) ~ ("->" | "-")
    | "?-" ~ opt(edge_refinement) ~ "-?"
    )
  
  def params: Parser[Any] = "(" ~ opt(param ~ (("," ~ param)*)) ~ ")"
  
  def param: Parser[Any] = (edge | variable | edge_refinement)
  
  def edge_refinement: Parser[Any] =
    ( opt(ident ~ opt("[" ~ ((ident ~ "=" ~ constant) | ident) ~ "]")) ~ ":" ~
        (ident | typeOf) ~ opt(type_constraint) ~ opt("<" ~ ident ~ ">")
    | ident
    )
  
  def type_constraint = "\\" ~ (ident | ("(" ~ repsep(ident, "+") ~ ")"))
  
  def variable = "var" ~ ident ~ ":" ~
    ( ident
    | "int" | "float" | "double" | "string"
    | ("map" ~ "<" ~ ident ~ "," ~ ident ~ ">")
    | ("set" ~ "<" ~ ident ~ ">")
    )
  
  def return_types =
    "(" ~ 
    opt((ident | "int" | "float" | "double" | "string") ~ 
          (("," ~ (ident | "int" | "float" | "double" | "string"))*)) ~
    ")"
  
  def assignment = ((ident ~ "." ~ ident) | visited) ~ "=" ~ expr  

}
